package cn.sppxpp;

import cn.sppxpp.annotations.Component;
import cn.sppxpp.annotations.MyAutoWired;
import cn.sppxpp.exception.BeanNotFoundException;
import cn.sppxpp.util.ClassLoaderUtil;
import cn.sppxpp.util.LogUtil;
import cn.sppxpp.util.PropertiesConfUtil;
import cn.sppxpp.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author spp
 * @date 2020-10-27 15:44
 * ico工厂
 **/
public class ApplicationContext {

    /**
     * 存放所有的bean
     */
    private final Map<String,Object> beans = new HashMap<>();
    private final String confFile;

    /**
     * 默认配置文件路径为根路径下的application.properties
     */
    public ApplicationContext() throws IOException {
        confFile = "/application.properties";
        LogUtil.log("ioc容器进行初始化");
        LogUtil.log("使用默认路径:"+confFile);
        init();
        LogUtil.log("ioc容器初始化完毕");
    }

    @Override
    public String toString() {
        return "ApplicationContext{" +
                "beans=" + beans +
                '}';
    }

    public ApplicationContext(String confFile) throws IOException {
        this.confFile = confFile;
        LogUtil.log("ioc容器进行初始化");
        init();
        LogUtil.log("ioc容器初始化完毕");
    }

    /**
     * ioc容器初始化
     */
    private void init() throws IOException {
        //读取配置文件
       /// PropertiesConfUtil propertiesConfUtil = new PropertiesConfUtil("application.properties");
        PropertiesConfUtil propertiesConfUtil = new PropertiesConfUtil();
        Properties properties = propertiesConfUtil.propertiesConf(confFile);

        //装载所有的class对象
        ClassLoaderUtil classLoader = new ClassLoaderUtil();
        classLoader.loadClass(properties);

        Map<String, Class<?>> allClass = classLoader.allClass;

        //写入ioc容器中
        allClass.forEach((key,value)->{
            try {
                hasAnnotation(value);
            } catch (InvocationTargetException | InstantiationException | NoSuchMethodException | IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        LogUtil.log("bean加载完毕:"+beans.size());
        //注入
        beans.forEach((k,v)->{
            try {
                injection(getBean(k));
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | BeanNotFoundException e) {
                e.printStackTrace();
            }
        });
        LogUtil.log("所有依赖都注入完毕");
    }


    /**
     * 判断是否加上注解
     */
    private void hasAnnotation(Class<?> c) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Component annotation = c.getAnnotation(Component.class);
        if (annotation != null){
            if(StringUtils.isNotEmpty(annotation.value())){
                iocPush(c,annotation.value());
            }else{
                iocPush(c,null);
            }
        }
    }

    /**
     * 加入到ioc容器中
     */
    private void iocPush(Class<?> c,String beanName) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        Object bean;
        try {
            bean = c.getConstructor().newInstance();
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodException("没有无参构造方法...");
        }
        if (beanName != null){
            beans.put(firstToLowerCase(beanName),bean);
            LogUtil.log(beanName + "加入到ioc容器中");
        }else {
            beans.put(firstToLowerCase(c.getSimpleName()),bean);
            LogUtil.log(c.getSimpleName() + "加入到ioc容器中");
        }
    }

    /**
     * 获取bean
     * @param beanName
     * @return
     */
    public  Object getBean(String beanName){
        if(beans.size() > 0){
            return beans.get(firstToLowerCase(beanName));
        }
        return null;
    }

    /**
     * 首字母转小写
     * @param str
     * @return
     */
    private String firstToLowerCase(String str){
        str = str.substring(0,1).toLowerCase() + str.substring(1);
        return str;
    }

    /**
     * 首字母转小写  前面在拼接set
     * @param str
     * @return
     */
    private String firstToUpCase(String str){
        str ="set" + str.substring(0,1).toUpperCase() + str.substring(1);
        return str;
    }

    /**
     * 依赖注入
     * @param o
     * @return
     */
    private void injection(Object o) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, BeanNotFoundException {
        Class<?> c = o.getClass();
        //获取所有的属性字段
        Field[] declaredFields = c.getDeclaredFields();
        for (Field field : declaredFields) {
            MyAutoWired annotation = field.getAnnotation(MyAutoWired.class);
            if(null != annotation){
                String beanName;
                if (StringUtils.isNotEmpty(annotation.value())){
                    beanName = annotation.value();
                }else {
                    beanName = field.getName();
                }
                Method declaredMethod;
                try {
                    declaredMethod = c.getDeclaredMethod(firstToUpCase(field.getName()),field.getType());
                } catch (NoSuchMethodException e) {
                    throw new NoSuchMethodException("方法"+firstToUpCase(field.getName())+"没有找到");
                }
                Object bean = getBean(beanName);
                if(null == bean){
                    throw new BeanNotFoundException(beanName + "没有在IOC容器中");
                }
                declaredMethod.invoke(o, bean);
                LogUtil.log(c.getSimpleName()+"的字段" + field.getName()+"注入成功");
            }
        }
    }

}
